/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package mars;

/**
 *
 * @author alexanderdealmeidapinto
 */
public class LineIntegralConvolutionMIPS {

    public static final int X = 0;
    public static final int Y = 1;
    private int L = 20;
    private float ds = 0.5f;
    private float vectorField[][][];
    private int[][] pixels;
    private int width;
    private int height;
    private float min = Float.MAX_VALUE;
    private float max = -Float.MAX_VALUE;
    private StringBuilder sb = null;

    public LineIntegralConvolutionMIPS(int[][] pixels, int width, int height, float[][][] vectorField) throws Exception {
        this.vectorField = vectorField;
        this.width = width;
        this.height = height;
        this.pixels = pixels;

        sb = new StringBuilder();

        writeData();

        writeCode();
    }

    private void writeData() {

        //Inicializa dados
        sb.append(".data\n\n");

        sb.append(String.format("pixelsOut: .float 0:%d\n", width * height));

        //public static final int X = 0;
        //public static final int Y = 1;
        sb.append(""
                //         + "X : .word 0\n"         // Não precisa criar essa variavel pois existe o registrador $zero
                + "Y : .word 1\n"
                + "\n");

        //private int L = 20;
        //private double ds = 0.5;
        sb.append(""
                + "L : .word 20\n"
                + "ds: .float 0.5\n"
                + "\n");

        //        private double vectorField[][][];
        sb.append(createVectorField());

        //        private int[][] pixels;
        sb.append(createPixels());

        //        private int width;
        //        private int height;
        sb.append(String.format(""
                + "width : .word %d\n"
                + "height: .word %d\n"
                + "\n", width, height));

        //        private double min = Double.MAX_VALUE;
        //        private double max = -Double.MAX_VALUE;
        sb.append(""
                + "min : .float 999999.0\n"
                + "max : .float -999999.0\n"
                + "\n");
    }

    public String createVectorField() {
        StringBuilder sbl = new StringBuilder("vectorField : .float ");

        for (int i = 0; i < vectorField.length; i++) {
            for (int j = 0; j < vectorField[i].length; j++) {
                for (int k = 0; k < 2; k++) {
                    if ((i == vectorField.length - 1) && (j == vectorField[i].length - 1) && (k == 1)) {
                        sbl.append(vectorField[i][j][k]);
                    } else {
                        sbl.append(vectorField[i][j][k] + ", ");
                    }

                }
            }
        }

        sbl.append("\n");

        return sbl.toString();
    }

    public String createPixels() {
        StringBuilder sbl = new StringBuilder("pixels: .word ");

        for (int i = 0; i < pixels.length; i++) {
            for (int j = 0; j < pixels[i].length; j++) {
                if ((i == pixels.length - 1) && (j == pixels[i].length - 1)) {
                    sbl.append(pixels[i][j]);
                } else {
                    sbl.append(pixels[i][j] + ", ");
                }
            }
        }

        sbl.append("\n");

        return sbl.toString();
    }

    // Escreve as instruções MIPS que resolvem o problema.
    private void writeCode() {
        sb.append(".text\n\n");
        sb.append(initRegisters());
        //sb.append(testGetVectorProcedure());
        //sb.append(testGetPixelValueProcedure());
        //sb.append(testGetConvolutionProcedure());

        
        sb.append(getFlowProcedure());
        sb.append("j exit\n");
        sb.append(getConvolutionProcedure());
        sb.append(getPixelValueProcedure());
        sb.append(getVectorProcedure());
        sb.append(getPixelDeslocProcedure());
        sb.append(getVectorDeslocProcedure());
        sb.append(getFloatModIntProcedure());
        sb.append(getExitProcedure());
    }

    /**
     * $zero recebe X
     * $s1 recebe Y
     * $s2 recebe width
     * $s3 recebe height
     * $s4 recebe L
     * $f0 recebe ds
     * $f2 recebe min
     * $f4 recebe max
     * @return
     */
    public String initRegisters() {
        return "#inicializa as variaveis que irao ficar fixas\n"
                + //"lw    $s0, X\n" +    // Nao precisa dessa linha pois existe o registrador $zero
                "lw    $s1, Y\n"
                + "lw    $s2, width\n"
                + "lw    $s3, height\n"
                + "lw    $s4, L\n"
                + "l.s  $f0, ds\n"
                + "l.s  $f1, min\n"
                + "l.s  $f2, max\n";
    }

    /**
     * Preenche pixels out com o resultado da convolucao em cada pixel
     * @return
     */
    private String getFlowProcedure() {
        StringBuilder sbl = new StringBuilder("\n\n\n"
                + "#Efetua a convolucao em cada pixel da matriz de pixels.\n"
                + "#################\n\n");

        //double conv;
        sbl.append("#double conv esta sendo representado por $f4\n");

        //for(int i = 0; i < width; i++)
        sbl.append("li     $s5, 0       #inicializacao for 1\n"
                + "loop1width:         #for(int i = 0; i < width; i++)\n"
                + "\n");


        //for(int j = 0; j < height; j++)
        sbl.append("li     $s6, 0       #inicializacao for 2\n"
                + "loop2heigth:        #for(int j = 0; j < height; j++)\n"
                + "\n");

        //conv = convolution(i, j);
        sbl.append("move    $a0, $s5        #conv = convolution(i, j);\n"
                + "move    $a1, $s6\n"
                + "jal     getconvolutionprocedure\n"
                + "mov.s   $f4, $f3\n"
                + "\n");

        //if(conv < min) min = conv;
        sbl.append("c.lt.s      $f1, $f4        #if(conv < min)\n"
                + "bc1t        fimif1conv\n"
                + "mov.s       $f1, $f4        #min = conv\n"
                + "fimif1conv:\n"
                + "\n");

        //else if(conv > max) max = conv;
        sbl.append(""
                + "c.lt.s      $f4, $f2        #if(conv > max)\n"
                + "bc1t        fimif2conv\n"
                + "mov.s       $f2, $f4\n"
                + "fimif2conv:\n"
                + "\n");

        //pixelsOut[i][j] = conv;
        sbl.append(""
                + "mul     $t0, $s5, $s2        #pixelsOut[i][j] = conv;\n"
                + "add     $t0, $t0, $s6\n"
                + "li      $t1, 4\n"
                + "mul     $t0, $t0, $t1\n"
                + "s.s     $f4, pixelsOut($t0)\n");

        //Fim For 1
        sbl.append(""
                + "addi    $s6, $s6, 1             #fim if (i)\n"
                + "bne     $s6, $s3, loop2heigth    #Compara $s6 com heigth\n"
                + "\n");

        //Fim for 2
        sbl.append(""
                + "addi    $s5, $s5, 1              #fim if (j)\n"
                + "bne     $s5, $s2, loop1width     #Compara $s5 com width\n"
                + "\n");




        return sbl.toString();
    }

    private String getConvolutionProcedure() {
        StringBuilder sbl = new StringBuilder("\n\n\n"
                + "#Funcao que faz a convolucao de um pixel\n"
                + "#Recebe como parametros os seguintes dados.\n"
                + "# $a0 recebe x0\n"
                + "# $a1 recebe y0\n"
                + "#################\n"
                + "#Retorna:\n"
                + "# $f3 o valor do pixel convolucionado\n"
                + "#################\n"
                + "\n"
                + "getconvolutionprocedure:\n"
                + "\n");

        sbl.append(pushOnStack32b("$ra", "$s5", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f20", "$f21", "$f22"));



        //double[] v1 = vectorField[x0][y0];
        sbl.append(pushOnStack32b("$a0", "$a1"));
        sbl.append("\n"
                + "jal     getvectordeslocprocedure         #double[] v1 = vectorField[x0][y0]\n"
                + "l.s     $f4, vectorField($v0)\n"
                + "l.s     $f5, vectorField($v1)\n"
                + "\n");
        sbl.append(popOnStack32b("$a0", "$a1"));

        //double[] v2 = v1;
        sbl.append("\n"
                + "mov.s   $f6, $f4         #double[] v2 = v1\n"
                + "mov.s   $f7, $f5\n"
                + "\n");

        //double x1 = x0;
        sbl.append("\n"
                + "mtc1        $a0, $f8     #double x1 = x0;\n"
                + "cvt.s.w     $f8, $f8\n"
                + "\n");

        //double y1 = y0;
        sbl.append("\n"
                + "mtc1        $a1, $f9     #double y1 = y0;\n"
                + "cvt.s.w     $f9, $f9\n"
                + "\n");

        //double x2 = x0;
        sbl.append("\n"
                + "mtc1        $a0, $f10        #double x2 = x0;\n"
                + "cvt.s.w     $f10, $f10\n"
                + "\n");

        //double y2 = y0;
        sbl.append("\n"
                + "mtc1        $a1, $f11        #double y2 = y0;\n"
                + "cvt.s.w     $f11, $f11\n"
                + "\n");

        //;
        sbl.append(pushOnStack32b("$f3", "$f4", "$f5"));
        sbl.append("\n"
                + "mov.s    $f3, $f10       #double sum = getPixelValue(x0, y0)\n"
                + "mov.s    $f4, $f11\n"
                + "jal      getpixelvalueprocedure\n"
                + "mov.s    $f12, $f5\n"
                + "\n");
        sbl.append(popOnStack32b("$f3", "$f4", "$f5"));

        /**
         * double[] v1 = vectorField[x0][y0];   esta em $f4 e $f5
         * double[] v2 = v1;                    esta em $f6 e $f7
         * double x1 = x0;                      esta em $f8
         * double y1 = y0;                      esta em $f9
         * double x2 = x0;                      esta em $f10
         * double y2 = y0;                      esta em $f11
         * double sum = getPixelValue(x0, y0);  esta em $f12
         */
        sbl.append("\n"
                + "mtc1   $zero, $f20     #$f20 recebe 0 (zero)\n"
                + "\n");

        sbl.append("\n"
                + "mtc1       $s2, $f21       #$f21 recebe width\n"
                + "cvt.s.w    $f21, $f21\n"
                + "\n");

        sbl.append("\n"
                + "mtc1       $s3, $f22       #$f22 recebe width\n"
                + "cvt.s.w    $f22, $f22\n"
                + "\n");

        //for(int s = 0; s < L; s++)
        sbl.append("\n" + //Inicio do LOOP
                "li      $s5, 0         #for(int s = 0; s < L; s++)\n" + //L = S
                ""
                + "loop1:\n");

        //x1 = x1 + ds*v1[X];
        sbl.append("\n"
                + "mul.s      $f13, $f0, $f4            #x1 = x1 + ds*v1[X];\n"
                + "add.s      $f8, $f8, $f13\n"
                + "\n");

        //y1 = y1 + ds*v1[Y];
        sbl.append("\n"
                + "mul.s      $f13, $f0, $f5            #y1 = y1 + ds*v1[Y];\n"
                + "add.s      $f9, $f9, $f13\n"
                + "\n");

        //x2 = x2 - ds*v2[X];
        sbl.append("\n"
                + "mul.s      $f13, $f0, $f6            #x2 = x2 - ds*v2[X];\n"
                + "sub.s      $f10, $f10, $f13\n"
                + "\n");

        //y2 = y2 - ds*v2[Y];
        sbl.append("\n"
                + "mul.s      $f13, $f0, $f7            #y2 = y2 - ds*v2[Y];\n"
                + "sub.s      $f11, $f11, $f13\n"
                + "\n");



        /**
         * $f20 tem 0 (zero)
         * $f21 tem width
         * $f22 tem heigth
         */
        //if(x1 >= 0
        sbl.append("#if(x1 >= 0\n"
                + "c.lt.s     $f8, $f20\n"
                + "bc1t       fimif1\n"
                + "\n");

        //&& y1 >= 0
        sbl.append("#&& y1 >= 0\n"
                + "c.lt.s     $f9, $f20\n"
                + "bc1t       fimif1\n"
                + "\n");

        //&& x1 < width
        sbl.append("#&& x1 < width\n"
                + "c.le.s     $f21, $f8\n"
                + "bc1t       fimif1\n"
                + "\n");

        //&& y1 < height) {
        sbl.append("#&& y1 < height)\n"
                + "c.le.s     $f22, $f9\n"
                + "bc1t       fimif1\n"
                + "\n");

        //v1 = getVector(x1, y1, v1);
        sbl.append(pushOnStack32b("$f3", "$f4", "$f5", "$f6", "$f7", "$f8"));

        sbl.append("mov.s   $f3, $f8\n"
                + "mov.s   $f6, $f5\n"
                + "mov.s   $f5, $f4\n"
                + "mov.s   $f4, $f9\n"
                + "jal getvectorprocedure\n"
                + "mov.s   $f23, $f7\n"
                + "mov.s   $f24, $f8\n");

        sbl.append(popOnStack32b("$f3", "$f4", "$f5", "$f6", "$f7", "$f8"));

        sbl.append("#v1 = getVector(x1, y1, v1)\n"
                + "mov.s   $f4, $f23\n"
                + "mov.s   $f5, $f24\n"
                + "\n");

        //}
        sbl.append("fimif1:\n");

        //if(x2 >= 0 && y2 >= 0 && x2 < width && y2 < height) {
        //v2 = getVector(x2, y2, v2);
        //}

        //x2 >= 0
        sbl.append("#x2 >= 0\n"
                + "c.lt.s   $f10, $f20\n"
                + "bc1t     fimif2\n"
                + "\n");

        //y2 >= 0
        sbl.append("#y2 >= 0\n"
                + "c.lt.s   $f11, $f20\n"
                + "bc1t     fimif2\n"
                + "\n");

        //x2 < width
        sbl.append("#x2 < width\n"
                + "c.le.s     $f21, $f10\n"
                + "bc1t    fimif2\n"
                + "\n");

        //y2 < height
        sbl.append("#y2 < height\n"
                + "c.le.s     $f22, $f11\n"
                + "bc1t       fimif2\n"
                + "\n");

        //v2 = getVector(x2, y2, v2)
        sbl.append(pushOnStack32b("$f3", "$f4", "$f5", "$f6", "$f7", "$f8"));

        sbl.append(""
                + "mov.s  $f3, $f10\n"
                + "mov.s  $f4, $f11\n"
                + "mov.s  $f5, $f6\n"
                + "mov.s  $f6, $f7\n"
                + "jal    getvectorprocedure\n"
                + "mov.s   $f23, $f7\n"
                + "mov.s   $f24, $f8\n");

        sbl.append(popOnStack32b("$f3", "$f4", "$f5", "$f6", "$f7", "$f8"));

        sbl.append("#v2 = getVector(x2, y2, v2)\n"
                + "mov.s   $f6, $f23\n"
                + "mov.s   $f7, $f24\n"
                + "\n");


        sbl.append("fimif2:\n");

        sbl.append(pushOnStack32b("$f3", "$f4", "$f5"));
        //sum += getPixelValue(x1, y1);
        sbl.append("\n"
                + "mov.s   $f3, $f8           #sum += getPixelValue(x1, y1)\n"
                + "mov.s   $f4, $f9\n"
                + "jal     getpixelvalueprocedure\n"
                + "add.s   $f12, $f12, $f5\n"
                + "\n");
        sbl.append(popOnStack32b("$f3", "$f4", "$f5"));

        sbl.append(pushOnStack32b("$f3", "$f4", "$f5"));
        //sum += getPixelValue(x2, y2);
        sbl.append("\n"
                + "mov.s      $f3, $f10       #sum += getPixelValue(x2, y2)\n"
                + "mov.s      $f4, $f11\n"
                + "jal        getpixelvalueprocedure\n"
                + "add.s      $f12, $f12, $f5\n"
                + "\n");
        sbl.append(popOnStack32b("$f3", "$f4", "$f5"));


        sbl.append("addi    $s5, $s5, 1\n" //Fim do LOOP
                + "bne     $s5, $s4, loop1      #Fim do LOOP\n"
                + "\n");

        //return sum / (2*L + 1);
        sbl.append("\n"
                + "move       $t0, $s4        #return sum / (2*L + 1)\n"
                + "add        $t0, $t0, $t0\n"
                + "addi       $t0, $t0, 1\n"
                + "mtc1       $t0, $f3\n"
                + "cvt.s.w    $f3, $f3\n"
                + "div.s      $f3, $f12, $f3\n"
                + "\n");


        sbl.append(popOnStack32b("$ra", "$s5", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f20", "$f21", "$f22"));

        sbl.append("jr $ra\n");

        return sbl.toString();
    }

    /**
     * $f3 recebe o valor de x
     * $f4 recebe o valor de y
     * #######################
     * $f5 recebe o valor do pixel
     * @return
     */
    private String getPixelValueProcedure() {
        StringBuilder sbl = new StringBuilder("\n\n\n"
                + "#Funcao que obtem o valor do pixel\n"
                + "#Recebe como parametros os seguintes dados.\n"
                + "# $f3 recebe x\n"
                + "# $f4 recebe y\n"
                + "#################\n"
                + "#Retorna:\n"
                + "# $f5 o valor do pixel\n"
                + "#################\n"
                + "\n"
                + "getpixelvalueprocedure:\n"
                + "\n");

        sbl.append(pushOnStack32b("$v0", "$ra", "$f3", "$f4", "$f6", "$f7", "$f8", "$f9", "$f10", "$f30", "$a0", "$a1"));

        sbl.append("#x = Math.abs(x) % width\n"
                + "abs.s   $f3, $f3    #x = Math.abs(x);\n"
                + "mov.s   $f10, $f3\n"
                + "move    $a0, $s2\n"
                + "jal    getfloatmodintprocedure\n"
                + "mov.s  $f3, $f10\n"
                + "\n");


        sbl.append("#y = Math.abs(y) % heigth\n"
                + "abs.s   $f4, $f4    #x = Math.abs(y);\n"
                + "mov.s   $f10, $f4\n"
                + "move    $a0, $s3\n"
                + "jal     getfloatmodintprocedure\n"
                + "mov.s   $f4, $f10\n"
                + "\n");

        sbl.append("\n# int i = (int)x;\n"
                + "cvt.w.s $f30, $f3\n"
                + "mfc1 $t0,$f30\n"
                + "\n");

        sbl.append("# int j = (int)y;\n"
                + "cvt.w.s $f30, $f4\n"
                + "mfc1 $t1,$f30\n"
                + "\n");

        sbl.append("#int i1 = (i + 1) % width;\n"
                + "addi   $t2, $t0, 1\n"
                + "div    $t2, $s2\n"
                + "mfhi   $t2\n"
                + "\n");

        sbl.append("#int j1 = (j + 1) % height;\n"
                + "addi   $t3, $t1, 1\n"
                + "div    $t3, $s3\n"
                + "mfhi   $t3\n"
                + "\n");

        /**
         * Nesse ponto temos
         *  $f3 = abs(x) % width    [x]
         *  $f4 = abs(x) % heigth   [y]
         *  i = $t0
         *  j = $t1
         *  i1 = $t2
         *  j1 = $t3
         */
        //double f_iy = pixels[i][j] + (y - j)*(pixels[i][j1] - pixels[i][j]);
        sbl.append("\n"
                + "move   $a0, $t0      #pixels[i][j1]\n"
                + "move   $a1, $t3\n"
                + "jal getpixeldeslocprocedure\n"
                + "lw     $t4, pixels($v0)\n"
                + "\n");

        sbl.append("\n"
                + "move   $a0, $t0      #pixels[i][j]\n"
                + "move   $a1, $t1\n"
                + "jal getpixeldeslocprocedure\n"
                + "lw     $v0, pixels($v0)\n"
                + "\n");

        sbl.append("\n"
                + "sub    $t4, $t4, $v0\n   #(pixels[i][j1] - pixels[i][j])\n"
                + "\n");

        sbl.append("\n"
                + "mtc1    $t1, $f6     #(y - j)\n"
                + "cvt.s.w $f6, $f6\n"
                + "sub.s   $f6, $f4, $f6\n"
                + "\n");

        sbl.append("\n"
                + "mtc1       $t4, $f7      #(y - j)*(pixels[i][j1] - pixels[i][j])\n"
                + "cvt.s.w    $f7, $f7\n"
                + "mul.s      $f6, $f6, $f7\n"
                + "\n");

        sbl.append("\n"
                + "move   $a0, $t0      #pixels[i][j]\n"
                + "move   $a1, $t1\n"
                + "jal getpixeldeslocprocedure\n"
                + "lw     $v0, pixels($v0)\n"
                + "\n");

        sbl.append("\n"
                + "mtc1       $v0, $f7      #double f_iy = pixels[i][j] + (y - j)*(pixels[i][j1] - pixels[i][j]);\n"
                + "cvt.s.w    $f7, $f7\n"
                + "add.s      $f6, $f6, $f7\n"
                + "\n");

        //$f6 contem float f_iy

        //double f_i1y = pixels[i1][j] + (y - j)*(pixels[i1][j1] - pixels[i1][j]);

        sbl.append("\n"
                + "move   $a0, $t2          #pixels[i1][j1]\n"
                + "move   $a1, $t3\n"
                + "jal getpixeldeslocprocedure\n"
                + "lw     $t4, pixels($v0)\n"
                + "\n");

        sbl.append("\n"
                + "move   $a0, $t2          #pixels[i1][j]\n"
                + "move   $a1, $t1\n"
                + "jal getpixeldeslocprocedure\n"
                + "lw     $t5, pixels($v0)\n"
                + "\n");

        sbl.append("\n"
                + "sub        $t4, $t4, $t5     #(pixels[i1][j1] - pixels[i1][j])\n"
                + "\n");

        sbl.append("\n"
                + "mtc1    $t1, $f7           #(y - j)\n"
                + "cvt.s.w $f7, $f7\n"
                + "sub.s   $f7, $f4, $f7\n"
                + "\n");

        sbl.append("\n"
                + "mtc1       $t4, $f8          #(y - j)*(pixels[i1][j1] - pixels[i1][j])\n"
                + "cvt.s.w    $f8, $f8\n"
                + "mul.s      $f7, $f7, $f8\n"
                + "\n");

        sbl.append("\n"
                + "move   $a0, $t2              #pixels[i1][j]\n"
                + "move   $a1, $t1\n"
                + "jal getpixeldeslocprocedure\n"
                + "lw     $v0, pixels($v0)\n"
                + "\n");

        sbl.append("\n"
                + "mtc1       $v0, $f8          #double f_i1y = pixels[i1][j] + (y - j)*(pixels[i1][j1] - pixels[i1][j]);\n"
                + "cvt.s.w    $f8, $f8\n"
                + "add.s      $f7, $f7, $f8\n"
                + "\n");

        //$f6 contem float f_iy
        //$f7 contem float f_i1y

        //return f_iy + (x - i)*(f_i1y - f_iy);

        sbl.append("\n" + //$f8
                "sub.s  $f8, $f7, $f6\n         #f_i1y - f_iy\n"
                + "\n");

        sbl.append("\n" + //$f9
                "mtc1       $t0, $f9            #(x - i)\n"
                + "cvt.s.w    $f9, $f9\n"
                + "sub.s      $f9, $f3, $f9\n"
                + "\n");

        sbl.append("\n" + //$f8
                "mul.s  $f8, $f8, $f9\n         #(x - i)*(f_i1y - f_iy)\n"
                + "\n");

        sbl.append("\n"
                + "add.s      $f5, $f6, $f8     #f_iy + (x - i)*(f_i1y - f_iy)\n"
                + "\n");

        sbl.append(popOnStack32b("$v0", "$ra", "$f3", "$f4", "$f6", "$f7", "$f8", "$f9", "$f10", "$f30", "$a0", "$a1"));

        sbl.append("jr $ra\n");
        return sbl.toString();
    }

    /**
     *  //double x, double y, double[] v
     *
     *  #parametros
     *  $f3 recebe x
     *  $f4 recebe y
     *  $f5 recebe v[0]
     *  $f6 recebe v[1]
     * //////////////////
     *  $f7 e $f8 terão o retorno.
     * @return
     */
    private String getVectorProcedure() {
        StringBuilder sbl = new StringBuilder("\n\n\n"
                + "#Funcao que obtem o vertor\n"
                + "#Recebe como parametros os seguintes dados.\n"
                + "# $f3 recebe x\n"
                + "# $f4 recebe y\n"
                + "# $f5 recebe v[0]\n"
                + "# $f6 recebe v[1]\n"
                + "#################\n"
                + "#Retorna:\n"
                + "# $f7 o valor resultante referente a v1[0]\n"
                + "# $f8 o valor resultante referente a v1[1]\n"
                + "#################\n"
                + "\n"
                + "getvectorprocedure:\n"
                + "\n");

        sbl.append(pushOnStack32b("$f10", "$f11", "$f14", "$f15", "$ra"));

        //double[] v1 = vectorField[(int)x][(int)y];
        sbl.append(floatToIntMIPS("$f3", "$t0"));
        sbl.append(floatToIntMIPS("$f4", "$t1"));

        sbl.append("mult   $t0, $s2         #$s2 = x*width\n");
        sbl.append("mflo   $t2              #$t2 recebe resultado da multiplicacao.\n");
        sbl.append("add   $t2, $t2, $t2     #$t2 *= 2\n");
        sbl.append("add   $t3, $t1, $t1     #$t3 = height*2\n");
        sbl.append("add   $t4, $t2, $t3     #$t4 = x*width*2 + y*2 , $t4 tem o deslocamento para acessar o vetor na posicao correta\n");
        sbl.append("#Multiplica por 4 para dar a posicao correta ( cada float tem 4 bytes )\n");
        sbl.append("add   $t4, $t4, $t4\n");
        sbl.append("add   $t4, $t4, $t4\n\n");

        sbl.append("#if(v[X]*v1[X] + v[Y]*v1[Y] < 0)\n");
        sbl.append("l.s     $f7, vectorField($t4)\n");
        sbl.append("mul.s   $f10, $f5, $f7      #$f10 tem v[X]*v1[X]\n\n");

        sbl.append("addi    $t5, $t4, 4     #A word referente a v1[Y] e a word apos $t4\n");
        sbl.append("l.s     $f8, vectorField($t5)\n");
        sbl.append("mul.s   $f11, $f6, $f8  #$f11 tem v[Y]*v1[Y]\n\n");
        //Nessa posicao $t4 tem a posicao de v1[X] e $t5 tem a posicao de v1[Y]

        //if(v[X]*v1[X] + v[Y]*v1[Y] < 0)
        sbl.append("add.s   $f14, $f10, $f11     # -> v[X]*v1[X] + v[Y]*v1[Y]\n");
        sbl.append("mtc1    $zero, $f15          #Coloca zero no registrado $f15\n");
        sbl.append("c.lt.s  $f14, $f15           #compare less them single presicion, resultado no flag 0\n");
        sbl.append("bc1f    finish               #Se a flag 0 tiver em falso finalize o procedimento\n\n");

        //v1[X] = -v1[X];
        //v1[Y] = -v1[Y];
        sbl.append("neg.s   $f7, $f7             #v1[X] = -v1[X];\n");
        sbl.append("neg.s   $f8, $f8             #v1[Y] = -v1[Y];\n\n");

        sbl.append("finish: \n");
        sbl.append(popOnStack32b("$f10", "$f11", "$f14", "$f15", "$ra"));


        sbl.append("\njr $ra\n");

        return sbl.toString();
    }

    private String testGetConvolutionProcedure() {
        return ""
                + "li     $a0, 0\n"
                + "li     $a1, 0\n"
                + "jal getconvolutionprocedure\n";
    }

    private String testGetPixelValueProcedure() {
        return "\n"
                + ".data\n"
                + "valorx: .float 1.0\n"
                + "valory: .float 2.0\n"
                + "\n"
                + ".text\n"
                + "\n"
                + "l.s    $f3, valorx\n"
                + "l.s    $f4, valory\n"
                + "\n"
                + "jal    getpixelvalueprocedure\n"
                + "\n";
    }

    private String testGetVectorProcedure() {
        return "\n"
                + ".data \n"
                + "teste1: .float 0.0\n"
                + "teste2: .float 0.0\n"
                + "teste3: .float 2.0\n"
                + "teste4: .float 3.0\n"
                + "\n"
                + ".text"
                + "\n"
                + "l.s    $f3 teste1\n"
                + "l.s    $f4 teste2\n"
                + "l.s    $f5 teste3\n"
                + "l.s    $f6 teste4\n"
                + "\n"
                + "jal getvectorprocedure\n"
                + "\n";
    }

    /**
     * Salva na pilha os registradores passados no parametros
     * ex.:
     * pushOnStack32b("$s0", "$f0");
     * @param o
     * @return
     */
    private String pushOnStack32b(String... o) {
        StringBuilder sbl = new StringBuilder(String.format("\n"
                + "#Salvando registradores de 32 bits\n"
                + "addi $sp, $sp, -%d\n", o.length * 4));
        int desloc = 0;
        for (int i = 0; i < o.length; i++) {
            if (o[i].charAt(1) == 'f') {
                sbl.append(String.format("swc1 %s, %d($sp)      #Salvando %s na pilha\n", o[i], desloc, o[i]));
            } else {
                sbl.append(String.format("sw %s, %d($sp)      #Salvando %s na pilha\n", o[i], desloc, o[i]));
            }
            desloc += 4;
        }

        sbl.append("\n");
        return sbl.toString();
    }

    /**
     * Restaura da pilha os registradores passados no parametros
     * ex.:
     * popOnStack32b("$s0", "$f0");
     * @param o
     * @return
     */
    private String popOnStack32b(String... o) {
        StringBuilder sbl = new StringBuilder("\n"
                + "#Restaurando registradores de 32 bits\n");
        int desloc = 0;
        for (int i = 0; i < o.length; i++) {
            if (o[i].charAt(1) == 'f') {
                sbl.append(String.format("lwc1 %s, %d($sp)      #Restaurando %s na pilha\n", o[i], desloc, o[i]));
            } else {
                sbl.append(String.format("lw %s, %d($sp)      #Restaurando %s na pilha\n", o[i], desloc, o[i]));
            }
            desloc += 4;
        }

        sbl.append(String.format("addi $sp, $sp, %d\n", o.length * 4));
        return sbl.toString();
    }

    /**
     * Transforma um float do registrador floatReg para um inteiro que estara armazenado com intReg
     * @param floatReg
     * @param intReg
     * @return
     */
    private String floatToIntMIPS(String floatReg, String intReg) {
        return String.format(""
                + "\n#Convertendo %s (float) para %s (int)\n"
                + "cvt.w.s %s, %s\n"
                + "mfc1 %s, %s\n\n", floatReg, intReg, floatReg, floatReg, intReg, floatReg);
    }

    private String getVectorDeslocProcedure() {
        StringBuilder sbl = new StringBuilder("\n\n\n"
                + "#Funcao que obtem o deslocamento sobre o vectorField\n"
                + "#Recebe como parametros os seguintes dados.\n"
                + "# $a0 recebe o valor de x\n"
                + "# $a1 recebe o valor de y\n"
                + "#################\n"
                + "#Retorna:\n"
                + "# $v0 o valor do deslocamento que deve ser aplicado para o primeiro elemento\n"
                + "# $v1 o valor do deslocamento que deve ser aplicado para o segundo elemento\n"
                + "#################\n"
                + "\n"
                + "getvectordeslocprocedure:\n"
                + "\n");

        sbl.append(pushOnStack32b("$ra"));

        sbl.append("mul     $v0, $a0, $s2\n" + //x*width*2
                "add     $v0, $v0, $v0\n"
                + "add     $v1, $a1, $a1\n"
                + //y * 2
                "add     $v0, $v0, $v1\n"
                + "li      $v1, 4\n"
                + "mul     $v0, $v0, $v1\n"
                + "add     $v1, $v0, 4\n"
                + "\n");

        sbl.append(popOnStack32b("$ra"));

        sbl.append("jr $ra\n");
        return sbl.toString();
    }

    /**
     *
     * @return
     */
    private String getPixelDeslocProcedure() {
        StringBuilder sbl = new StringBuilder("\n\n\n"
                + "#Funcao que obtem o deslocamento para o pixel\n"
                + "#Recebe como parametros os seguintes dados.\n"
                + "# $a0 recebe o valor de x\n"
                + "# $a1 recebe o valor de y\n"
                + "#################\n"
                + "#Retorna:\n"
                + "# $v0 o valor do deslocamento que deve ser aplicado\n"
                + "#################\n"
                + "\n"
                + "getpixeldeslocprocedure:\n"
                + "\n");

        sbl.append(pushOnStack32b("$ra"));

        sbl.append("mul     $v0, $a0, $s2\n" + //$s2 tem a lagura da matriz de pixels
                "add     $v0, $v0, $a1\n"
                + "li      $v1, 4\n"
                + "mul     $v0, $v0, $v1\n"
                + "\n");

        sbl.append(popOnStack32b("$ra"));

        sbl.append("jr $ra\n");
        return sbl.toString();
    }

    /**
     * Efetua a operação de um Float Mod Int
     * @return
     */
    private String getFloatModIntProcedure() {
        StringBuilder sbl = new StringBuilder("\n\n\n"
                + "#Funcao que obtem o vertor\n"
                + "#Recebe como parametros os seguintes dados.\n"
                + "# $f10 recebe o valor float\n"
                + "# $a0 recebe o valor int\n"
                + "#################\n"
                + "#Retorna:\n"
                + "# $f10 contem o resultado do mod\n"
                + "#################\n"
                + "\n"
                + "getfloatmodintprocedure:\n"
                + "\n");

        sbl.append(pushOnStack32b("$f0", "$t0", "$ra"));

        sbl.append(""
                + "floor.w.s  $f0, $f10\n"
                + "cvt.s.w    $f0, $f0\n"
                + "sub.s      $f10, $f10, $f0\n"
                + "cvt.w.s    $f0, $f0\n"
                + "mfc1       $t0, $f0\n"
                + "div        $t0, $a0\n"
                + "mfhi       $t0\n"
                + "mtc1       $t0, $f0\n"
                + "cvt.s.w    $f0, $f0\n"
                + "add.s      $f10, $f10, $f0\n"
                + "\n");

        sbl.append(popOnStack32b("$f0", "$t0", "$ra"));


        sbl.append("jr $ra\n");
        return sbl.toString();
    }

    /**
     * Finaliza a execução do programa.
     * @return
     */
    private String getExitProcedure() {
        return "\n"
                + "#Procedimento que encerra a execucao do programa\n"
                + "exit:    \n"
                + "li       $v0, 10        # Servico 10 indica saida.\n"
                + "syscall                 # Executa a saida.\n";
    }

    /**
     * Retorna o codigo MIPS gerado
     * @return
     */
    @Override
    public String toString() {
        
        if (sb == null) {
            return "";
        } else {
            return sb.toString();
        }
    }
}
